Introduce free trial countdown pop-up#84049
Conversation
|
Hey, I noticed you changed If you want to automatically generate translations for other locales, an Expensify employee will have to:
Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running: npx ts-node ./scripts/generateTranslations.ts --helpTypically, you'd want to translate only what you changed by running |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
@shubham1206agra Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
Asking for the Spanish translation: https://expensify.slack.com/archives/C01GTK53T8Q/p1772555174811579 |
|
@Expensify/design can you please review this PR to verify the pop-up styles? Thanks |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f21e45eaa1
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@dylanexpensify I found 1 issue: We're checking the trial plan by using |
|
@dylanexpensify by the way, we also need BE changes to allow for updating |
|
Can we get mobile screenshots as well? Desktop is looking good |
JmillsExpensify
left a comment
There was a problem hiding this comment.
We saw a big uplift with this modal. Let's get it in product!
Re: this, free trials only apply to workspaces. So if no workspace, no free trial. |
|
Thank you, @dylanexpensify. Can you please request a BE member to handle this issue |
@dubielzyk-expensify I added the video for iOS Safari. Can you please take a look? I'll record the videos for other platforms when the BE is ready |
|
That looks right to me. @Expensify/design for extra 👀 |
|
@shubham1206agra Thanks for your feedback, I fixed |
|
@dukenv0307 Please apply fix similar to #89910 |
|
Done |
|
@dukenv0307 What I meant was apply the similar fix to free countdown pop-up modal, not copy the same code over. |
|
🚧 @grgia has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
@shubham1206agra Ah, I got it. Could you please take a look again? |
|
@shubham1206agra Thanks for your feedback! I fixed them all |
|
🚧 @grgia has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
@dukenv0307 What is |
| trialPaymentReminder: { | ||
| title: 'Stay ahead of the deadline', | ||
| subtitle: "Don't wait until the last minute, add your payment method today to ensure continuous access to your expenses on Expensify.", | ||
| trialEndsInDays: ({days}: {days: number}) => `Trial ends in ${days} ${days === 1 ? 'day' : 'days'}`, |
There was a problem hiding this comment.
@dukenv0307 Please use pluralization API here. See other translations using like common.downloadFailedEmptyReportDescription
w_4eec33a8ce51e88a053b31c12a3e30dbf6a0e3a6-2026-05-15.18_05_10.876.mov@dukenv0307 I am able to dismiss modal using ESC key. Not sure if this is a bug or not. |
| function getTrialStartupGraceRemainingMs(firstDayFreeTrial: string | undefined): number { | ||
| if (!firstDayFreeTrial) { | ||
| return 0; | ||
| } | ||
| const trialStartMs = new Date(`${firstDayFreeTrial}Z`).getTime(); | ||
| if (!Number.isFinite(trialStartMs)) { | ||
| return 0; | ||
| } | ||
| return Math.max(trialStartMs + FIVE_MINUTES_MS - Date.now(), 0); | ||
| } |
There was a problem hiding this comment.
@dukenv0307 The logic is flawed here. If the firstDayFreeTrial is absent you are marking grace period as 0, which will mark the state as ready immediately. You should mark this to a really high number.
|
@dukenv0307 Bug: Not seeing the modal without refresh after 5 minute window has elapsed. |
@dukenv0307 Bug: Modal backdrop is missing on a refresh |
|
@dukenv0307 Your test steps are not working |
Oh, I just copied it in the issue description |
|
Weird, it worked on my side. Lemme check again |

Explanation of Change
Fixed Issues
$ #84022
PROPOSAL:
Tests
Case 1: With 1, 2, 3, 7 and 15 days
Title: "Stay Ahead of the Deadline”
Subtitle: “Don’t wait until the last minute, add your payment method today to ensure continuous access to your expenses on Expensify.”
Button: “Add payment card” (redirecting to /settings/subscription/add-payment-card)
Button: “Close” (closes the pop-up)
Case 2: With 28, 29
“Trial ends in X days” (3 or 2 days)
Title: ”Stay Ahead of the Deadline”
Subtitle: “Don’t wait until the last minute, add your payment method today to ensure continuous access to your expenses on Expensify.”
Button: “Add payment card” (to /settings/subscription/add-payment-card)
Button: “Close” (closes the pop-up)
Case 3: On the last 24h
“Trial ends in 00h : 00m : 00s” (Countdown)
Title: ”Stay Ahead of the Deadline”
Subtitle: “Don’t wait until the last minute, add your payment method today to ensure continuous access to your expenses on Expensify.”
Button: “Add payment card” (redirecting to /settings/subscription/add-payment-card)
Button: “Close” (closes the pop-up)
Case 4: Open the free trial countdown popup after the app review modal
Offline tests
QA Steps
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
Screen.Recording.2026-03-04.at.21.36.43.mov
MacOS: Chrome / Safari
Screen.Recording.2026-03-03.at.23.13.16.mov
Screen.Recording.2026-03-03.at.23.13.40.mov